import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

import start from '@/start'
import core from '@/core'
import config from './config'
import dbSet from './dbSet'

/**
 * 当前项目代码。如果要请求的云服务空间绑定了多个项目，则需要写死，正常留空即可
 * 启动的url接口。如果为空，则使用StartCloud启动
 * 启动的云服务空间信息。优先使用StartUrl
 */
const {TargetCode, StartUrl, StartCloud, NoData, NoLocal} = start

// 系统初始化需要读取的本地存储信息，读取成功之后向state中赋值，所以要求与state中的字段名一致
const StorageKey = {
	// 用户Token
	Authorization: 'KeyForAuthorization',
	// 启动信息
	StartInfo: 'KeyForStartInfo',
}

// 不需要写入store的信息，仅系统初始化时使用，或者仅在当前页面内使用
const OnceData = {
	StartInfo: undefined, // 项目启动信息
	/**
	 * 是否已经开启了调试
	 * 关于调试
	 * 0：完全不开启
	 * 	1：仅在生产发布状态下开启。一般要求一定是测试环境
	 * 	2：仅在开发调试状态下开启
	 * 	3：开发调试状态和生产发布状态都开启
	 * 开启后会被赋值为true
	 */
	openVConsole: 3,
}

// 域名初始化，结尾不含/
const domainInitial = (path) => {
	if (path.endsWith('/')) {
		path = path.substring(0, path.length - 1)
		path = domainInitial(path)
	}
	return path
}

const myPlugin = store => {
	console.log('插件初始化')
	store.subscribe((mutation, state) => {
		console.log('状态发生变化', mutation.type, state)
	})
}

const store = new Vuex.Store({
	strict: !core.isDevelopment,
	plugins: [myPlugin],
	state: {
		// 是否完全无本地请求模式。该模式下全部请求都用云函数云对象或者URL完成，并且可以删除core/request/async/index.js中的相关代码
		NoLocal,
		// 开发模式下是否是无数据请求模式，以免UI热更新引起频繁请求。仅开发模式下生效
		NoData: core.isDevelopment ? NoData : false,
		/**
		 * 系统的错误信息。将会跳转到错误页面呈现
		 * 0：没有错误
		 * 1：项目初始化失败
		 * 2：项目启动失败
		 * 3：没有权限
		 * 4：项目未上架
		 * 5：项目已过期
		 */
		error: '',
		/**
		 * 系统初始化
		 * 完成后才开始页面初始化或其它
		 */
		isInitial: false,
		/**
		 * 请求token
		 * 在登录成功后获取并存储于本地
		 * 仅存储登录成功后的token。系统启动验证的时候是验证这一条！
		 * 其余每次请求的token存储于core.request.token
		 */
		Authorization: '',
		// 系统配置
		config: {
			
		},
		/**
		 * 当前环境是否是测试环境。决定了是否使用测试数据库
		 * 由start中的isTest决定
		 * 如果其isTest是布尔值，直接决定
		 * 如果是0，表示开发状态下是测试环境；生产状态下是正式环境
		 * 如果是1，表示开发状态下是测试环境；生产状态下是读取项目设置信息
		 */
		isTest: true,
		// 用户是否同意了服务协议
		readAgreement: true,
		// 记录当前的域名。结尾不含/。H5时以当前域名为准，否则以项目设置的域名为准
		domain: '',
	},
	getters: {
		
	},
	mutations: {
		// 系统初始化完成
		setInitial(state, data) {
			state.isInitial = data
		},
		// 设置系统错误信息
		setError(state, error) {
			state.error = error
		},
		// 登录之后获取token，并存储于本地
		setAuthorization(state, data) {
			state.Authorization = data
			if (data) {
				uni.setStorageSync(StorageKey.Authorization, data)
			} else {
				uni.removeStorageSync(StorageKey.Authorization)
			}
		},
		// 设置配置信息
		setConfig(state, data) {
			if (data) {
				state.config = data
			} else {
				state.config = config
			}
		},
		// 设置真实的是否测试环境
		setIsTest(state, data) {
			state.isTest = data
		},
		// 设置用户是否同意了服务协议
		setReadAgreement(state, data = !state.readAgreement) {
			state.readAgreement = data
		},
		// 设置当前域名
		setDomain(state, data) {
			state.domain = data
		},
	},
	actions: {
		/**
		 * 系统的错误信息。将会跳转到错误页面呈现
		 * 0：没有错误
		 * 1：项目初始化失败
		 * 2：项目启动失败
		 * 3：没有权限
		 * 4：项目未上架
		 * 5：项目已过期
		 */
		systemError({state, commit }, error) {
			state.error = error
			uni.navigateTo({
				url: '/pages/error/error'
			})
		},
		/**
		 * 开启调试信息（根据代码设定，是否强制开启调试）
		 * 。0：完全不开启
		 * 	1：仅在生产发布状态下开启。一般要求一定是测试环境
		 * 	2：仅在开发调试状态下开启
		 * 	3：开发调试状态和生产发布状态都开启
		 * 需require('vconsole')和require('@/uni_modules/HF-HF_debugging/common/next.js')支持
		 */
		openVConsole({state, commit }, open = OnceData.openVConsole) {
			// #ifdef APP || H5
			if (OnceData.openVConsole === true) {
				// 已经开启调试
			} else {
				if (open) {
					if (open === 3 || (open === 1 && core.isDevelopment) || (open === 2 && core.isDevelopment)) {
						// 开启调试
						// #ifdef APP
						// 类Vconsole APP端调试工具 - HF调试器 https://ext.dcloud.net.cn/plugin?id=6147
						// const {HFdebugging} = require('@/uni_modules/HF-HF_debugging/common/next.js')
						// new HFdebugging()
						// #endif
						// #ifdef H5
						const VConsole = require('vconsole')
						new VConsole()
						// #endif
						OnceData.openVConsole = true
					}
				}
			}
			// #endif
			console.log('当前版本：' + core.store.verson.current.code)
			console.log('更新时间：' + core.store.verson.current.time)
			console.log('版本说明：' + core.store.verson.current.remark)
		},
		/**
		 * 初始化
		 * 1，调试信息初始化
		 * 2，设备信息初始化
		 * 3，路由信息初始化
		 * 4，多语言信息初始化
		 * 5，存储信息初始化
		 * 6，实际根据当前发布的地址，向公共云服务空间请求以获取当前项目配置；开发状态下可手动指定
		 */
		initial({ state, commit, dispatch }) {
			/**
			 * core引用state，在不方便获取$store的时候直接使用core.state
			 */
			core.state = state
			commit('setConfig')
			// 调试信息初始化
			dispatch('openVConsole')
			// 设备信息初始化
			core.store.device.initial()
			// 路由信息初始化
			core.router.initial()
			// 缓存清理信息初始化
			core.storage.initial()
			// 设置默认语言。后端会做处理，必须是既定的语言之一，否则会设置为默认的语言类型
			core.language.type = core.store.device.info.hostLanguage
			/**
			 * 存储信息初始化
			 * 1，读取必要的信息
			 * 2，初始化请求的缓存。读取所有的请求缓存key，需要删除的删除，需要保留的保留
			 * 初始化请求的缓存，该功能暂时不做，等请求缓存做好了一起做
			 */
			Object.keys(StorageKey).forEach(key => {
				var res = uni.getStorageSync(StorageKey[key])
				if (res) {
					if (Object.keys(OnceData).includes(key)) {
						OnceData[key] = res
					} else {
						commit(`set${key}`, res, true)
					}
				}
			})
			if (OnceData.StartInfo) {
				if (state.NoData) {
					// 开发模式下，可直接使用本地的启动信息
					console.log('启动信息读取缓存：', OnceData.StartInfo)
					return dispatch('handleStartSuccess', false)
				}
			}
			// 读取存储的信息
			core.storage.before_initial(TargetCode)
			// 启动参数准备。如果云服务空间有多个项目绑定，则需要传code，否则后端自动获取
			const params = {
				code: TargetCode,
				user_id: core.user.info._id, // 不建议使用该_id，建议使用Authorization
				Authorization: state.Authorization,
				api: core.request.api.date,
				basedata: core.request.basedata.date,
				fixcolumns: core.request.fixcolumns.date,
				basevalue: core.info.basevalue.updatetimestamp,
				theme: core.theme.updatetimestamp,
				language: `${core.language.type}+${core.language.date}`,
				Apply: core.store.device.info.apply_code,
				Development: core.isDevelopment,
				// 记录启动页
				pageKey: core.router.getPageKey(),
				// 记录启动页参数。当前还没有，如果需要，需需改page.vue中获取参数的时机
				// pageParams: router.getPageParams(),
				// 代码设置的是否测试环境
				isTest: start.isTest,
				// 记录启动域名，H5通过该参数判别是否测试环境
				domain: window ? domainInitial(window.location.href.split('#')[0]) : ''
			}
			// 开发状态下是否更新信息。正常需要去改线上的更新值
			if (core.isDevelopment) {
				const updateKeys = ['api', 'basedata', 'fixcolumns']
				updateKeys.forEach(key => {
					params[key] = '0'
				})
			}
			const handleSuccess = (res) => {
				console.log('系统启动成功：', res)
				OnceData.StartInfo = res
				if (core.isDevelopment) { // 这一步仅开发状态下执行。而且也仅是state.NoData为true时执行
					uni.setStorage({
						key: StorageKey.StartInfo,
						data: res
					})
				}
				if (res.token) {
					commit('setAuthorization', res.token)
				}
				// 启动信息获取成功并更新成功
				dispatch('handleStartSuccess', true)
			}
			const handleFail = (err) => {
				console.error('系统启动失败：', err)
				dispatch('systemError', 2)
			}
			/**
			 * 启动请求仅是常规登录，仅获取用户基础信息，没有做特殊的登录
			 * 在实际项目里应该会有当前项目的特别登录信息，如当前项目的权限等
			 * 方案：启动时读取特别的登录信息，在项目登录时存储该特别信息。在handleSuccess里面执行handleLoginData
			 */
			if (StartUrl) {
				uni.request({
					url: StartUrl,
					method: 'GET',
					data: params,
					success: handleSuccess,
					fail: handleFail
				})
			} else {
				const cloud = uniCloud.init(StartCloud).importObject('a-initial')
				if (cloud) {
					cloud.start(params).then(handleSuccess).catch(handleFail)
				} else {
					console.error('启动云函数不存在')
					dispatch('systemError', 2)
				}
			}
		},
		
		/**
		 * 启动信息获取成功并更新成功
		 * res：是否是通过启动请求来的。false是开发状态下读取本地存储来的
		 */
		handleStartSuccess({ state, commit, dispatch }, res) {
			if (OnceData.StartInfo) {
				const {token, isTest} = OnceData.StartInfo
				if (res) {
					// 更新启动信息
					core.storage.after_initial(TargetCode, OnceData.StartInfo)
				} else {
					// 直接读取启动信息即可
					core.storage.before_initial(TargetCode)
				}
				// 获取专属的登录信息。需要项目code+用户id
				dispatch('handleLoginData')
				// 给当前需要的域名赋值
				var domainPath = ''
				if (core.info.target.domain && core.info.target.domain.length > 0) {
					core.info.target.domain.map(path => domainInitial(path))
					domainPath = core.info.target.domain[0]
				}
				if (window) { // 优先取当前正在使用的域名
					domainPath = domainInitial(window.location.href.split('#')[0])
				}
				// 给Tabbar赋值
				if (core.info.target.tabbar && core.info.target.tabbar.length > 0) {
					const list = []
					core.info.target.tabbar.forEach(item => {
						if (!item.hide) { // 不显示的不添加进来
							list.push(item)
						}
					})
					// 默认显示第一个
					core.info.tabbar.index = 0
					core.info.tabbar.height = 50
					core.info.tabbar.list = list
				} else {
					core.info.tabbar.index = -1
				}
				// 基础赋值
				commit('setDomain', domainPath)
				commit('setIsTest', isTest)
				// 初始化完成
				commit('setInitial', true)
				// #ifdef H5
				// 执行数据库的更新操作。该方法仅开发状态下的H5时执行
				dbSet && dbSet()
				// #endif
			} else {
				console.error('没有启动信息')
				dispatch('systemError', 2)
			}
		},
		
		// 登录成功。一般要存储并跳转
		handleLoginSuccess({ state, commit, dispatch }, res) {
			if (res.token) {
				commit('setAuthorization', res.token)
				// 启动时是user，不用再处理；直接登录时是data；复杂登录时是user，data是该复杂登录的内容
				const action = (user) => {
					core.storage.user_initial(core.info.target.code, user)
				}
				if (res.user || res.data) {
					if (res.user) {
						res.user.data = res.data
					}
					action(res.user || res.data)
				} else {
					// 获取会员信息
					core.request.fetchData('initial', 'getUserInfo', res.token).then(res => {
						action(res.data)
					})
				}
			}
		},
		
		// 登录过期，需要重新登录。登录完成后要执行回调，有回调的情况，一般是登录之后再继续操作；没有回调的情况就是正常完成登录
		handleLoginExpired({ state, commit, dispatch }, callback) {
			const {loginPages, logunPages} = config
			var hasLogin, current = core.router.getPageKey()
			if (core.info.target.openMust) {
				hasLogin = logunPages.includes(current) ? false : true
			} else {
				hasLogin = loginPages.includes(current) ? true : false
			}
			console.log('是否需要登录：', hasLogin)
			console.log('当前页面标识：', current)
			if (hasLogin) { // 要求一定要登录的
				// 登录过期
				uni.showToast({
					title: '登录过期，请重新登录',
					icon: 'none'
				})
				// 登出
				dispatch('onLogout', callback)
			} else {
				callback && callback()
			}
		},
		
		// 点击了登出
		onLogout({ state, commit, dispatch }, callback) {
			return new Promise((resolve, reject) => {
				commit('setAuthorization', '')
				const complete = (res) => {
					core.user.initial() // 用户信息初始化
					const {pages, getPageKey} = core.router
					const pageKey = getPageKey()
					if (pageKey && pages[pageKey] && pages[pageKey].path) {
						if (pages[pageKey].path.includes('workbench/')) {
							core.router.to('站点登录', undefined, 3)
						}
					}
					callback && callback(res)
					resolve(res)
				}
				core.request.fetchData('initial', 'logout').then(complete).catch(complete)
			})
		},
		
		/**
		 * 点击了需要登录后才能完成的事件
		 * 
		 * 已登录的话，直接执行回调
		 * 未登录的话，弹出登录对话框，登录成功后执行回调
		 * 
		 * type：0对话框登录；1：页面登录
		 */
		onLogin({ state, commit, dispatch }, callback, type = 0) {
			if (core.user.info._id) {
				callback && callback()
			} else {
				if (type === 0) {
					const self = core.router.getCurrentPageObj()
					if (self.$refs.login) {
						self.$refs.login.open(callback)
					} else {
						core.showToast('当前页面获取对话框错误')
					}
				} else {
					core.router.to('站点登录', {callback})
				}
			}
		},
		
		// 处理项目登录的专属数据。data不存在时是读取，data存在时是写入。需要项目code+用户id
		handleLoginData({ state, commit, dispatch }, data) {
			if (core.info.target.code && core.user.info._id) {
				const key = `KeyFor${core.info.target.code}-${core.user.info._id}`
				if (data) {
					uni.setStorage({key, data})
				} else {
					core.user.info.data = uni.getStorageSync(key)
				}
			}
		}
	}
})

export default store